package com.xian.cloud.config;

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.concurrent.GlobalEventExecutor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.Random;

/**
 * @Author: xlr
 * @Date: Created in 下午9:37 2019/2/14
 */
@Component
@Qualifier("textWebSocketFrameHandler")
@ChannelHandler.Sharable
public class TextWebSocketFrameHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

    public static ChannelGroup channels = new DefaultChannelGroup( GlobalEventExecutor.INSTANCE );

    @Autowired
    RedisTemplate redisTemplate;

    /*
    * (non-Javadoc)
    * 覆盖了 handlerAdded() 事件处理方法。
    * 每当从服务端收到新的客户端连接时
    */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        System.out.println( ctx.channel().remoteAddress() );
        String uName = getRandomName();  //用来获取一个随机的用户名，可以用其他方式代替

        Channel incoming = ctx.channel();
        for (Channel channel : channels) {
            channel.writeAndFlush( new TextWebSocketFrame( "[新用户] - " + uName + " 加入" ) );
        }

        redisTemplate.opsForValue().set( incoming.id() + "", uName );   //存储用户
        channels.add( ctx.channel() );
    }
    /*
     * (non-Javadoc)
     * .覆盖了 handlerRemoved() 事件处理方法。
     * 每当从服务端收到客户端断开时
     */
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        Channel incoming = ctx.channel();
        String uName = (String) redisTemplate.opsForValue().get( String.valueOf( incoming.id() ) );
        for (Channel channel : channels) {
            channel.writeAndFlush( new TextWebSocketFrame( "[用户] - " + uName + " 离开" ) );
        }
        redisTemplate.delete( String.valueOf( incoming.id() ) );   //删除用户
        redisTemplate.opsForValue().set( "cacheName", ((String) redisTemplate.opsForValue().get( "cacheName" )).replaceAll( uName, "" ) );   //标准已经使用的用户名
        channels.remove( ctx.channel() );
    }
    /*
     * 覆盖channelActive 方法在channel被启用的时候触发（在建立连接的时候）
     * 覆盖了 channelActive() 事件处理方法。服务端监听到客户端活动
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        Channel incoming = ctx.channel();
        System.out.println( "用户:" + (String) redisTemplate.opsForValue().get( incoming.id() + "" ) + "在线" );
    }

    /*
     * 客户端 失去连接
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        Channel incoming = ctx.channel();
        System.out.println( "用户:" + (String) redisTemplate.opsForValue().get( incoming.id() + "" ) + "掉线" );
    }

    /*
     * exceptionCaught() 事件处理方法是当出现 Throwable 对象才会被调用，
     * 即当 Netty 由于 IO 错误或者处理器在处理事件时抛出的异常时。
     * 在大部分情况下，捕获的异常应该被记录下来并且把关联的 channel 给关闭掉。
     * 然而这个方法的处理方式会在遇到不同异常的情况下有不同的实现，
     * 比如你可能想在关闭连接之前发送一个错误码的响应消息。
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
            throws Exception {
        Channel incoming = ctx.channel();
        System.out.println( "用户:" + (String) redisTemplate.opsForValue().get( incoming.id() + "" ) + "异常" );
        cause.printStackTrace();
        ctx.close();
    }

    /*
    * 覆盖了 channelRead0() 事件处理方法。
    * 每当从服务端读到客户端写入信息时，
    * 其中如果你使用的是 Netty 5.x 版本时，
    * 需要把 channelRead0() 重命名为messageReceived()
    */
    @Override
    protected void messageReceived(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
        Channel incoming = ctx.channel();
        String uName = (String) redisTemplate.opsForValue().get( incoming.id() + "" );
        for (Channel channel : channels) {
            if (channel != incoming) {
                channel.writeAndFlush( new TextWebSocketFrame( "[" + uName + "]" + msg.text() ) );
            } else {
                channel.writeAndFlush( new TextWebSocketFrame( "[you]" + msg.text() ) );
            }
        }
    }
    static String abc = "qwertyuiopasdfghjklzxcvbnm";

    private String getRandomName(){
        Random random = new Random(  );
        StringBuilder sb = new StringBuilder(  );
        for(int i = 0;i<5;i++){
            int j = random.nextInt(25);
            String substring = abc.substring( j ,j+1);
            sb.append( substring );
        }
        return sb.toString();
    }
}